package cn.edu.fudan.data;

import cn.edu.fudan.tools.BinarySearch;
import cn.edu.fudan.type.DataItem;

import java.util.ArrayList;
import java.util.List;

public class SlideWindow {

	private List<List<DataItem>> datas;
	private static BinarySearch bSearch = new BinarySearch();

	private List<DataItem> getSlideWindow(List<DataItem> data, long timepoint, long window_length) {

		List<DataItem> window = new ArrayList<>();

		long halfw = window_length / 2;
		long w_begin = timepoint - halfw;
		int w_begin_index = bSearch.binarySearch(data, w_begin, 0);
		long w_end = timepoint + halfw;
		int w_end_index = bSearch.binarySearch(data, w_end, 1);

		if (w_begin_index * w_end_index < 0) {   //保证了文章中的滑动窗口的两个策略？？？？？？？？好像只有参数输错了才能返回
			return window;
		}

		double max_val = 0;
		long max_point = timepoint;

		// find max point in the range of time point to build a new window
		for (int i = w_begin_index; i <= w_end_index; i++) {
			if (data.get(i).getValue() > max_val) {
				max_val = data.get(i).getValue();
				max_point = data.get(i).getTimestamp();
			}
		}

		w_begin = max_point - halfw;
		int w_begin_index_new = bSearch.binarySearch(data, w_begin, 0);

		w_end = max_point + halfw;
		int w_end_index_new = bSearch.binarySearch(data, w_end, 1);

		 if(w_begin_index_new < w_begin_index){
		 for(int i = w_begin_index_new; i <= w_begin_index; i ++){
		 if(data.get(i).getValue() > max_val){
		 return window;
		 }
		 }
		 }
		 else if(w_end_index_new > w_end_index){
		 for(int i = w_end_index; i <= w_end_index_new; i ++){
		 if(data.get(i).getValue() > max_val){
		 return window;
		 }
		 }
		 }

		for (int i = w_begin_index_new; i <= w_end_index_new; i++) {
			DataItem di = new DataItem();
			di.setTimestamp(data.get(i).getTimestamp());
			di.setValue(data.get(i).getValue());

			window.add(di);
		}

		return window;
	}

	private List<List<DataItem>> getSlideWindow(List<DataItem> data_posi, List<DataItem> data_nega, long timepoint,
			long window_length) {
		List<List<DataItem>> windows = new ArrayList<>();

		long halfw = window_length / 2;
		long w_begin = timepoint - halfw;
		int w_begin_index = bSearch.binarySearch(data_posi, w_begin, 0);
		long w_end = timepoint + halfw;
		int w_end_index = bSearch.binarySearch(data_posi, w_end, 1);

		if (w_begin_index * w_end_index < 0) {
			return windows;
		}

		double max_val = 0;
		long max_point = timepoint;

		// find max point in the range of time point to build a new window
		for (int i = w_begin_index; i <= w_end_index; i++) {
			if (data_posi.get(i).getValue() > max_val) {
				max_val = data_posi.get(i).getValue();
				max_point = data_posi.get(i).getTimestamp();
			}
		}

		w_begin = max_point - halfw;
		int w_begin_index_new = bSearch.binarySearch(data_posi, w_begin, 0);

		w_end = max_point + halfw;
		int w_end_index_new = bSearch.binarySearch(data_posi, w_end, 1);

		List<DataItem> window = new ArrayList<>();
		for (int i = w_begin_index_new; i <= w_end_index_new; i++) {
			DataItem di = new DataItem();
			di.setTimestamp(data_posi.get(i).getTimestamp());
			di.setValue(data_posi.get(i).getValue());

			window.add(di);
		}
		if (window.size() > 0) {
			windows.add(window);
			window = new ArrayList<>();
			int w_begin_index_nega = bSearch.binarySearch(data_nega, w_begin, 0);
			int w_end_index_nega = bSearch.binarySearch(data_nega, w_end, 1);
			if (w_begin_index_nega >= 0 && w_end_index_nega >= 0) {
				for (int i = w_begin_index_nega; i <= w_end_index_nega; i++) {
					DataItem di = new DataItem();
					di.setTimestamp(data_nega.get(i).getTimestamp());
					di.setValue(data_nega.get(i).getValue());

					window.add(di);
				}
			}
			windows.add(window);
		}

		return windows;
	}

	public List<List<DataItem>> slideWindow(List<DataItem> data, List<Long> lable, long window_length) {
		for (Long timepoint : lable) {
			List<DataItem> slideData = getSlideWindow(data, timepoint, window_length);
			datas.add(slideData);
		}
		return datas;
	}

	public List<List<DataItem>> extractWindow(List<DataItem> data, long window_length) {
		List<List<DataItem>> datas = new ArrayList<>();
		long ws = 0;
		long we = 0;

		for (int i = 0; i < data.size(); i++) {
			long begin = data.get(i).getTimestamp();
			List<DataItem> window = getSlideWindow(data, begin, window_length);

			if (window.size() > 0 && window.get(0).getTimestamp() != ws
					&& window.get(window.size() - 1).getTimestamp() != we) {
				datas.add(window);
				ws = window.get(0).getTimestamp();
				we = window.get(window.size() - 1).getTimestamp();
			}

		}
		return datas;
	}

	public List<List<DataItem>> extractWindow(List<DataItem> data, List<Long> timestamp, long window_length) {
		List<List<DataItem>> datas = new ArrayList<>();
		long ws = 0;
		long we = 0;

		for (int i = 0; i < timestamp.size(); i++) {
			long begin = timestamp.get(i);
			List<DataItem> window = getSlideWindow(data, begin, window_length);
			if (window.size() > 0 && window.get(0).getTimestamp() != ws
					&& window.get(window.size() - 1).getTimestamp() != we) {
				datas.add(window);
				ws = window.get(0).getTimestamp();
				we = window.get(window.size() - 1).getTimestamp();
			}
		}
		return datas;
	}

	public List<List<List<DataItem>>> extractWindow(List<DataItem> data_posi, List<DataItem> data_nega,
			List<Long> timestamp, long window_length) {
		List<List<List<DataItem>>> datas = new ArrayList<>();

		long ws = 0;
		long we = 0;
		
		for (int i = 0; i < timestamp.size(); i++) {
			long begin = timestamp.get(i);
			List<List<DataItem>> windows = getSlideWindow(data_posi, data_nega, begin, window_length);
			if (windows.size() > 0 && windows.get(0).size() > 0 && windows.get(0).get(0).getTimestamp() != ws
					&& windows.get(0).get(windows.get(0).size() - 1).getTimestamp() != we) {
				List<List<DataItem>> data = new ArrayList<>();
				data.addAll(windows);
				datas.add(data);
				ws = windows.get(0).get(0).getTimestamp();
				we = windows.get(0).get(windows.get(0).size() - 1).getTimestamp();
			}
		}
		return datas;
	}
}
